This one just worked
fn energize(map: &[&[u8]], start: ((i32, i32), (i32, i32))) -> usize {
let mut seen: HashMap<(i32, i32), Vec<(i32, i32)>> = HashMap::new();
let mut rays = vec![start];
while let Some(((mut x, mut y), (mut dx, mut dy))) = rays.pop() {
while (0..map[0].len() as i32).contains(&x) && (0..map.len() as i32).contains(&y) {
// No need to repeat a pos+dir we've already seen.
let entry = seen.entry((x, y)).or_default();
if entry.contains(&(dx, dy)) {
break;
} else {
entry.push((dx, dy));
}
match (map[y as usize][x as usize], dx, dy) {
(b'\\', _, _) => (dx, dy) = (dy, dx),
(b'/', _, _) => (dx, dy) = (-dy, -dx),
(b'|', _, 0) | (b'-', 0, _) => {
rays.push(((x - dy, y - dx), (-dy, -dx)));
(dx, dy) = (dy, dx);
}
_ => {}
}
(x, y) = (x + dx, y + dy)
}
}
seen.len()
}
Here's the code I wrote for both parts. I'm pretty happy with how concise the reflection logic is, just one short match
.
For part two I literally just did this for every possible case. It runs in about 350ms, which is way more than I'm usually happy with; which makes me think that there's some clever way to store sub-results of rays to share calculations between calls. I'm pretty certain there's tons of repeats there.
Maybe store the energized count for every reflector tile+direction combination? And then every time we run into a reflector of some kind, we might already have a cached result. Would have to do some work to avoid double-counting and the like, though. Should be possible.
Unfortunately, that's all hypothetical for me, for I have a train to catch, lmao.